package com.idanchuang.component.redis.util;

import com.idanchuang.component.base.exception.common.ErrorCode;
import com.idanchuang.component.base.exception.core.ExFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import static com.idanchuang.component.redis.config.DRedisLockConfig.DEFAULT_TIMEOUT;
import static com.idanchuang.component.redis.config.DRedisLockConfig.DEFAULT_TRY_LOCK_TIMEOUT;

/**
 * 基于Redis的分布式锁
 * @author yjy
 * @date 2019/11/27 11:07
 **/
public class DRedisLocks {

    private static final Logger log = LoggerFactory.getLogger(DRedisLocks.class);

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param runnable 要执行的代码块
     */
    public static void runWithLock(String lockName, Runnable runnable) {
        runWithLock(lockName, DEFAULT_TRY_LOCK_TIMEOUT, DEFAULT_TIMEOUT, false, runnable);
    }

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param autoRenew 是否自动续期
     * @param runnable 要执行的代码块
     */
    public static void runWithLock(String lockName, boolean autoRenew, Runnable runnable) {
        runWithLock(lockName, DEFAULT_TRY_LOCK_TIMEOUT, DEFAULT_TIMEOUT, autoRenew, runnable);
    }

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param callable 要执行的代码块
     * @param <V> 返回类型
     * @return 执行结果
     */
    public static <V> V runWithLock(String lockName, Callable<V> callable) {
        return runWithLock(lockName, DEFAULT_TRY_LOCK_TIMEOUT, DEFAULT_TIMEOUT, false, callable);
    }

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param autoRenew 是否自动续期
     * @param callable 要执行的代码块
     * @param <V> 返回类型
     * @return 执行结果
     */
    public static <V> V runWithLock(String lockName, boolean autoRenew, Callable<V> callable) {
        return runWithLock(lockName, DEFAULT_TRY_LOCK_TIMEOUT, DEFAULT_TIMEOUT, autoRenew, callable);
    }

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param tryTimeout 获取锁的超时时间
     * @param runnable 要执行的代码块
     */
    public static void runWithLock(String lockName, long tryTimeout, Runnable runnable) {
        runWithLock(lockName, tryTimeout, DEFAULT_TIMEOUT, false, runnable);
    }

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param tryTimeout 获取锁的超时时间
     * @param autoRenew 是否自动续期
     * @param runnable 要执行的代码块
     */
    public static void runWithLock(String lockName, long tryTimeout, boolean autoRenew, Runnable runnable) {
        runWithLock(lockName, tryTimeout, DEFAULT_TIMEOUT, autoRenew, runnable);
    }

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param tryTimeout 获取锁的超时时间
     * @param callable 要执行的代码块
     * @param <V> 返回类型
     * @return 执行结果
     */
    public static <V> V runWithLock(String lockName, long tryTimeout, Callable<V> callable) {
        return runWithLock(lockName, tryTimeout, DEFAULT_TIMEOUT, false, callable);
    }

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param tryTimeout 获取锁的超时时间
     * @param autoRenew 是否自动续期
     * @param callable 要执行的代码块
     */
    public static <V> V runWithLock(String lockName, long tryTimeout, boolean autoRenew, Callable<V> callable) {
        return runWithLock(lockName, tryTimeout, DEFAULT_TIMEOUT, autoRenew, callable);
    }

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param tryTimeout 获取锁的超时时间
     * @param lockTimeout 持有锁的超时时间
     * @param runnable 要执行的代码块
     */
    public static void runWithLock(String lockName, long tryTimeout, long lockTimeout, Runnable runnable) {
       runWithLock(lockName, tryTimeout, lockTimeout, false, runnable);
    }

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param tryTimeout 获取锁的超时时间
     * @param autoRenew 是否自动续期
     * @param runnable 要执行的代码块
     */
    public static void runWithLock(String lockName, long tryTimeout, long lockTimeout, boolean autoRenew, Runnable runnable) {
        Callable<Void> callable = () -> {
            runnable.run();
            return null;
        };
        runWithLock(lockName, tryTimeout, lockTimeout, autoRenew, callable);
    }

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param tryTimeout 获取锁的超时时间
     * @param lockTimeout 持有锁的超时时间
     * @param callable 要执行的代码块
     * @param <V> 返回类型
     * @return 执行结果
     */
    public static <V> V runWithLock(String lockName, long tryTimeout, long lockTimeout, Callable<V> callable) {
        return runWithLock(lockName, tryTimeout, lockTimeout, false, callable);
    }

    /**
     * 执行分布式同步代码块
     * @param lockName 锁名称
     * @param tryTimeout 获取锁的超时时间
     * @param lockTimeout 持有锁的超时时间
     * @param autoRenew 是否自动续期
     * @param callable 要执行的代码块
     * @param <V> 返回类型
     * @return 执行结果
     */
    public static <V> V runWithLock(String lockName, long tryTimeout, long lockTimeout, boolean autoRenew, Callable<V> callable) {
        DRedisLock lock = new DRedisLock(lockName, lockTimeout, autoRenew);
        log.debug("Init DRedisLock > {}", lock);
        try {
            if (lock.tryLock(tryTimeout, TimeUnit.MILLISECONDS)) {
                log.debug("Lock successful, lockName: {}", lockName);
                return callable.call();
            }
            throw ExFactory.throwWith(ErrorCode.CONFLICT, "Get redisLock failed, lockName: " + lockName);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw ExFactory.throwWith(ErrorCode.CONFLICT, e.getMessage());
        } finally {
            lock.unlock();
            log.debug("Unlock successful, lockName: {}", lockName);
        }
    }

}
